
/**
 * @file:       verification.cpp
 * @author:     WubinWang
 * @contact:    wubinstu@163.com
 * @date:       2025-01-01
 * @license:    MIT License
 *
 * Copyright (c) 2025 WubinWang
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in all
 * copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.
 *
 * DISCLAIMER:
 * 1. User acknowledges all application risks and consequences arising from usage
 *    of this software are exclusively borne by the user.
 * 2. The original author shall not be held liable for any direct, indirect,
 *    incidental, special, exemplary or consequential damages.
 * 3. No warranty of fitness for particular purpose or non-infringement is provided. */


#ifndef __VERIFICATION_HPP_
#define __VERIFICATION_HPP_


#include <cstdint>


// CRC8 Algorithms
#define CRC_ALGO_CRC8                static_cast<std::uint8_t> (0x07), static_cast<std::uint8_t> (0x00), static_cast<std::uint8_t> (0x00), static_cast<bool> (0), static_cast<bool> (0)
#define CRC_ALGO_CRC8_SAE_J1850      static_cast<std::uint8_t> (0x1D), static_cast<std::uint8_t> (0xFF), static_cast<std::uint8_t> (0xFF), static_cast<bool> (0), static_cast<bool> (0)
#define CRC_ALGO_CRC8_SAE_J1850_ZERO static_cast<std::uint8_t> (0x1D), static_cast<std::uint8_t> (0x00), static_cast<std::uint8_t> (0x00), static_cast<bool> (0), static_cast<bool> (0)
#define CRC_ALGO_CRC8_8H2F           static_cast<std::uint8_t> (0x2F), static_cast<std::uint8_t> (0xFF), static_cast<std::uint8_t> (0xFF), static_cast<bool> (0), static_cast<bool> (0)
#define CRC_ALGO_CRC8_CDMA2000       static_cast<std::uint8_t> (0x9B), static_cast<std::uint8_t> (0xFF), static_cast<std::uint8_t> (0x00), static_cast<bool> (0), static_cast<bool> (0)
#define CRC_ALGO_CRC8_DARC           static_cast<std::uint8_t> (0x39), static_cast<std::uint8_t> (0x00), static_cast<std::uint8_t> (0x00), static_cast<bool> (1), static_cast<bool> (1)
#define CRC_ALGO_CRC8_DVB_S2         static_cast<std::uint8_t> (0xD5), static_cast<std::uint8_t> (0x00), static_cast<std::uint8_t> (0x00), static_cast<bool> (0), static_cast<bool> (0)
#define CRC_ALGO_CRC8_EBU            static_cast<std::uint8_t> (0x1D), static_cast<std::uint8_t> (0xFF), static_cast<std::uint8_t> (0x00), static_cast<bool> (1), static_cast<bool> (1)
#define CRC_ALGO_CRC8_ICODE          static_cast<std::uint8_t> (0x1D), static_cast<std::uint8_t> (0xFD), static_cast<std::uint8_t> (0x00), static_cast<bool> (0), static_cast<bool> (0)
#define CRC_ALGO_CRC8_ITU            static_cast<std::uint8_t> (0x07), static_cast<std::uint8_t> (0x00), static_cast<std::uint8_t> (0x55), static_cast<bool> (0), static_cast<bool> (0)
#define CRC_ALGO_CRC8_MAXIM          static_cast<std::uint8_t> (0x31), static_cast<std::uint8_t> (0x00), static_cast<std::uint8_t> (0x00), static_cast<bool> (1), static_cast<bool> (1)
#define CRC_ALGO_CRC8_ROHC           static_cast<std::uint8_t> (0x07), static_cast<std::uint8_t> (0xFF), static_cast<std::uint8_t> (0x00), static_cast<bool> (1), static_cast<bool> (1)
#define CRC_ALGO_CRC8_WCDMA          static_cast<std::uint8_t> (0x9B), static_cast<std::uint8_t> (0x00), static_cast<std::uint8_t> (0x00), static_cast<bool> (1), static_cast<bool> (1)

// CRC16 Algorithms
#define CRC_ALGO_CRC16_CCIT_ZERO   static_cast<std::uint16_t> (0x1021), static_cast<std::uint16_t> (0x0000), static_cast<std::uint16_t> (0x0000), static_cast<bool> (0), static_cast<bool> (0)
#define CRC_ALGO_CRC16_ARC         static_cast<std::uint16_t> (0x8005), static_cast<std::uint16_t> (0x0000), static_cast<std::uint16_t> (0x0000), static_cast<bool> (1), static_cast<bool> (1)
#define CRC_ALGO_CRC16_IBM         static_cast<std::uint16_t> (0x8005), static_cast<std::uint16_t> (0x0000), static_cast<std::uint16_t> (0x0000), static_cast<bool> (1), static_cast<bool> (1)
#define CRC_ALGO_CRC16_AUG_CCITT   static_cast<std::uint16_t> (0x1021), static_cast<std::uint16_t> (0x1D0F), static_cast<std::uint16_t> (0x0000), static_cast<bool> (0), static_cast<bool> (0)
#define CRC_ALGO_CRC16_BUYPASS     static_cast<std::uint16_t> (0x8005), static_cast<std::uint16_t> (0x0000), static_cast<std::uint16_t> (0x0000), static_cast<bool> (0), static_cast<bool> (0)
#define CRC_ALGO_CRC16_CCITT_FALSE static_cast<std::uint16_t> (0x1021), static_cast<std::uint16_t> (0xFFFF), static_cast<std::uint16_t> (0x0000), static_cast<bool> (0), static_cast<bool> (0)
#define CRC_ALGO_CRC16_CDMA2000    static_cast<std::uint16_t> (0xC867), static_cast<std::uint16_t> (0xFFFF), static_cast<std::uint16_t> (0x0000), static_cast<bool> (0), static_cast<bool> (0)
#define CRC_ALGO_CRC16_DDS_110     static_cast<std::uint16_t> (0x8005), static_cast<std::uint16_t> (0x800D), static_cast<std::uint16_t> (0x0000), static_cast<bool> (0), static_cast<bool> (0)
#define CRC_ALGO_CRC16_DECT_R      static_cast<std::uint16_t> (0x0589), static_cast<std::uint16_t> (0x0000), static_cast<std::uint16_t> (0x0001), static_cast<bool> (0), static_cast<bool> (0)
#define CRC_ALGO_CRC16_DECT_X      static_cast<std::uint16_t> (0x0589), static_cast<std::uint16_t> (0x0000), static_cast<std::uint16_t> (0x0000), static_cast<bool> (0), static_cast<bool> (0)
#define CRC_ALGO_CRC16_DNP         static_cast<std::uint16_t> (0x3D65), static_cast<std::uint16_t> (0x0000), static_cast<std::uint16_t> (0xFFFF), static_cast<bool> (1), static_cast<bool> (1)
#define CRC_ALGO_CRC16_EN_13757    static_cast<std::uint16_t> (0x3D65), static_cast<std::uint16_t> (0x0000), static_cast<std::uint16_t> (0xFFFF), static_cast<bool> (0), static_cast<bool> (0)
#define CRC_ALGO_CRC16_GENIBUS     static_cast<std::uint16_t> (0x1021), static_cast<std::uint16_t> (0xFFFF), static_cast<std::uint16_t> (0xFFFF), static_cast<bool> (0), static_cast<bool> (0)
#define CRC_ALGO_CRC16_MAXIM       static_cast<std::uint16_t> (0x8005), static_cast<std::uint16_t> (0x0000), static_cast<std::uint16_t> (0xFFFF), static_cast<bool> (1), static_cast<bool> (1)
#define CRC_ALGO_CRC16_MCRF4XX     static_cast<std::uint16_t> (0x1021), static_cast<std::uint16_t> (0xFFFF), static_cast<std::uint16_t> (0x0000), static_cast<bool> (1), static_cast<bool> (1)
#define CRC_ALGO_CRC16_RIELLO      static_cast<std::uint16_t> (0x1021), static_cast<std::uint16_t> (0xB2AA), static_cast<std::uint16_t> (0x0000), static_cast<bool> (1), static_cast<bool> (1)
#define CRC_ALGO_CRC16_T10_DIF     static_cast<std::uint16_t> (0x8BB7), static_cast<std::uint16_t> (0x0000), static_cast<std::uint16_t> (0x0000), static_cast<bool> (0), static_cast<bool> (0)
#define CRC_ALGO_CRC16_TELEDISK    static_cast<std::uint16_t> (0xA097), static_cast<std::uint16_t> (0x0000), static_cast<std::uint16_t> (0x0000), static_cast<bool> (0), static_cast<bool> (0)
#define CRC_ALGO_CRC16_TMS37157    static_cast<std::uint16_t> (0x1021), static_cast<std::uint16_t> (0x89EC), static_cast<std::uint16_t> (0x0000), static_cast<bool> (1), static_cast<bool> (1)
#define CRC_ALGO_CRC16_USB         static_cast<std::uint16_t> (0x8005), static_cast<std::uint16_t> (0xFFFF), static_cast<std::uint16_t> (0xFFFF), static_cast<bool> (1), static_cast<bool> (1)
#define CRC_ALGO_CRC16_A           static_cast<std::uint16_t> (0x1021), static_cast<std::uint16_t> (0xC6C6), static_cast<std::uint16_t> (0x0000), static_cast<bool> (1), static_cast<bool> (1)
#define CRC_ALGO_CRC16_CCITT       static_cast<std::uint16_t> (0x1021), static_cast<std::uint16_t> (0x0000), static_cast<std::uint16_t> (0x0000), static_cast<bool> (1), static_cast<bool> (1)
#define CRC_ALGO_CRC16_KERMIT      static_cast<std::uint16_t> (0x1021), static_cast<std::uint16_t> (0x0000), static_cast<std::uint16_t> (0x0000), static_cast<bool> (1), static_cast<bool> (1)
#define CRC_ALGO_CRC16_MODBUS      static_cast<std::uint16_t> (0x8005), static_cast<std::uint16_t> (0xFFFF), static_cast<std::uint16_t> (0x0000), static_cast<bool> (1), static_cast<bool> (1)
#define CRC_ALGO_CRC16_X_25        static_cast<std::uint16_t> (0x1021), static_cast<std::uint16_t> (0xFFFF), static_cast<std::uint16_t> (0xFFFF), static_cast<bool> (1), static_cast<bool> (1)
#define CRC_ALGO_CRC16_XMODEM      static_cast<std::uint16_t> (0x1021), static_cast<std::uint16_t> (0x0000), static_cast<std::uint16_t> (0x0000), static_cast<bool> (0), static_cast<bool> (0)
#define CRC_ALGO_CRC16_XMODEM2     static_cast<std::uint16_t> (0x8408), static_cast<std::uint16_t> (0x0000), static_cast<std::uint16_t> (0x0000), static_cast<bool> (1), static_cast<bool> (1)

// CRC32 Algorithms
#define CRC_ALGO_CRC32         static_cast<std::uint32_t> (0x04'C1'1D'B7), static_cast<std::uint32_t> (0xFF'FF'FF'FF), static_cast<std::uint32_t> (0xFF'FF'FF'FF), static_cast<bool> (1), static_cast<bool> (1)
#define CRC_ALGO_CRC32_BZIP2   static_cast<std::uint32_t> (0x04'C1'1D'B7), static_cast<std::uint32_t> (0xFF'FF'FF'FF), static_cast<std::uint32_t> (0xFF'FF'FF'FF), static_cast<bool> (0), static_cast<bool> (0)
#define CRC_ALGO_CRC32_C       static_cast<std::uint32_t> (0x1E'DC'6F'41), static_cast<std::uint32_t> (0xFF'FF'FF'FF), static_cast<std::uint32_t> (0xFF'FF'FF'FF), static_cast<bool> (1), static_cast<bool> (1)
#define CRC_ALGO_CRC32_D       static_cast<std::uint32_t> (0xA8'33'98'2B), static_cast<std::uint32_t> (0xFF'FF'FF'FF), static_cast<std::uint32_t> (0xFF'FF'FF'FF), static_cast<bool> (1), static_cast<bool> (1)
#define CRC_ALGO_CRC32_MPEG2   static_cast<std::uint32_t> (0x04'C1'1D'B7), static_cast<std::uint32_t> (0xFF'FF'FF'FF), static_cast<std::uint32_t> (0x00'00'00'00), static_cast<bool> (0), static_cast<bool> (0)
#define CRC_ALGO_CRC32_POSIX   static_cast<std::uint32_t> (0x04'C1'1D'B7), static_cast<std::uint32_t> (0x00'00'00'00), static_cast<std::uint32_t> (0xFF'FF'FF'FF), static_cast<bool> (0), static_cast<bool> (0)
#define CRC_ALGO_CRC32_Q       static_cast<std::uint32_t> (0x81'41'41'AB), static_cast<std::uint32_t> (0x00'00'00'00), static_cast<std::uint32_t> (0x00'00'00'00), static_cast<bool> (0), static_cast<bool> (0)
#define CRC_ALGO_CRC32_JAMCRC  static_cast<std::uint32_t> (0x04'C1'1D'B7), static_cast<std::uint32_t> (0xFF'FF'FF'FF), static_cast<std::uint32_t> (0x00'00'00'00), static_cast<bool> (1), static_cast<bool> (1)
#define CRC_ALGO_CRC32_XFER    static_cast<std::uint32_t> (0x00'00'00'AF), static_cast<std::uint32_t> (0x00'00'00'00), static_cast<std::uint32_t> (0x00'00'00'00), static_cast<bool> (0), static_cast<bool> (0)
#define CRC_ALGO_CRC32_KOOPMAN static_cast<std::uint32_t> (0x74'1B'8C'D7), static_cast<std::uint32_t> (0xFF'FF'FF'FF), static_cast<std::uint32_t> (0xFF'FF'FF'FF), static_cast<bool> (1), static_cast<bool> (1)

// CRC64 Algorithms
#define CRC_ALGO_CRC64_ECMA     static_cast<std::uint64_t> (0x42'F0'E1'EB'A9'EA'36'93), static_cast<std::uint64_t> (0xFF'FF'FF'FF'FF'FF'FF'FF), static_cast<std::uint64_t> (0xFF'FF'FF'FF'FF'FF'FF'FF), static_cast<bool> (1), static_cast<bool> (1)
#define CRC_ALGO_CRC64_ECMA_182 static_cast<std::uint64_t> (0x42'F0'E1'EB'A9'EA'36'93), static_cast<std::uint64_t> (0x00'00'00'00'00'00'00'00), static_cast<std::uint64_t> (0x00'00'00'00'00'00'00'00), static_cast<bool> (0), static_cast<bool> (0)
#define CRC_ALGO_CRC64_ISO      static_cast<std::uint64_t> (0x00'00'00'00'00'00'00'1B), static_cast<std::uint64_t> (0xFF'FF'FF'FF'FF'FF'FF'FF), static_cast<std::uint64_t> (0xFF'FF'FF'FF'FF'FF'FF'FF), static_cast<bool> (1), static_cast<bool> (1)
#define CRC_ALGO_CRC64_WE       static_cast<std::uint64_t> (0x42'F0'E1'EB'A9'EA'36'93), static_cast<std::uint64_t> (0xFF'FF'FF'FF'FF'FF'FF'FF), static_cast<std::uint64_t> (0xFF'FF'FF'FF'FF'FF'FF'FF), static_cast<bool> (0), static_cast<bool> (0)
#define CRC_ALGO_CRC64_XZ       static_cast<std::uint64_t> (0x42'F0'E1'EB'A9'EA'36'93), static_cast<std::uint64_t> (0xFF'FF'FF'FF'FF'FF'FF'FF), static_cast<std::uint64_t> (0xFF'FF'FF'FF'FF'FF'FF'FF), static_cast<bool> (1), static_cast<bool> (1)

struct Verification_Base_st {
    virtual ~Verification_Base_st () = default;
};

template <typename STDINT>
class Verification_Calculator_ct : public Verification_Base_st {
  public:
    explicit Verification_Calculator_ct (const Verification_Calculator_ct & _other) noexcept        = default;
    explicit Verification_Calculator_ct (Verification_Calculator_ct && _other) noexcept             = default;
    Verification_Calculator_ct & operator= (const Verification_Calculator_ct & _other) noexcept     = default;
    Verification_Calculator_ct & operator= (Verification_Calculator_ct && _other) noexcept          = default;
    explicit Verification_Calculator_ct () noexcept                                                 = default;
    virtual ~Verification_Calculator_ct () noexcept                                                 = default;

    virtual inline auto calculate (const std::uint8_t _data[], unsigned _length) noexcept -> STDINT = 0;
    virtual inline auto accumulate (const std::uint8_t _data[], unsigned _length) noexcept -> void  = 0;
    virtual inline auto accumulated () noexcept -> STDINT                                           = 0;
    virtual inline auto resetCurrent () noexcept -> void                                            = 0;
};

template <typename STDINT>
class CRC_Calculator_ct : public Verification_Calculator_ct<STDINT> {
  public:
    explicit CRC_Calculator_ct (const CRC_Calculator_ct & _other) noexcept    = default;
    explicit CRC_Calculator_ct (CRC_Calculator_ct && _other) noexcept         = default;
    CRC_Calculator_ct & operator= (const CRC_Calculator_ct & _other) noexcept = default;
    CRC_Calculator_ct & operator= (CRC_Calculator_ct && _other) noexcept      = default;
    explicit CRC_Calculator_ct () noexcept                                    = default;
    virtual ~CRC_Calculator_ct () noexcept                                    = default;

    explicit CRC_Calculator_ct (STDINT _ploy, STDINT _init, STDINT _xorOut, bool _refIn, bool _refOut) noexcept :
        _m_refIn {_refIn}, _m_refOut {_refOut}, _m_ploy {_ploy}, _m_init {_init}, _m_current {_init}, _m_xor_out {_xorOut} {
        CRC_Calculator_ct::generateTable (this->_m_ploy, this->_m_table);
    }

    virtual inline auto updateConfig (STDINT _ploy, STDINT _init, STDINT _xorOut, bool _refIn, bool _refOut) noexcept -> void {
        this->_m_ploy    = _ploy;
        this->_m_init    = _init;
        this->_m_xor_out = _xorOut;
        this->_m_refIn   = _refIn;
        this->_m_refOut  = _refOut;

        CRC_Calculator_ct::generateTable (this->_m_ploy, this->_m_table);
    }

    static inline auto generateTable (STDINT _ploy, STDINT _table[256]) noexcept -> void {
        for (auto i = 0; i < 256; ++i) {
            STDINT crc = static_cast<STDINT> (i) << (sizeof (STDINT) * 8 - 8);
            for (auto j = 0; j < 8; ++j) {
                if (crc & (static_cast<STDINT> (1) << (sizeof (STDINT) * 8 - 1)))
                    crc = (crc << 1) ^ _ploy;
                else crc <<= 1;
            }
            _table[i] = crc;
        }
    }

    static inline auto reflect (STDINT data, unsigned nBits) noexcept -> STDINT {
        STDINT reflection = 0;
        for (unsigned bit = 0; bit < nBits; ++bit) {
            if (data & 0X01)
                reflection |= (static_cast<STDINT> (1) << ((nBits - 1) - bit));
            data >>= 1;
        }
        return reflection;
    }

    virtual inline auto calculate (const std::uint8_t _data[], unsigned _length) noexcept -> STDINT override {
        (void) this->resetCurrent ();
        (void) this->accumulate (_data, _length);
        return this->accumulated ();
    }

    /// @brief continuously accumulating
    /// @note You should call this->resetCurrent () first every accumulate cycle
    virtual inline auto accumulate (const std::uint8_t _data[], unsigned _length) noexcept -> void override {
        for (unsigned i = 0; i < _length; ++i) {
            std::uint8_t byte = _data[i];
            if (this->_m_refIn)
                byte = CRC_Calculator_ct::reflect (byte, 8);
            this->_m_current = this->_m_table[((this->_m_current >> (sizeof (STDINT) * 8 - 8)) ^ byte) & 0xFF] ^ (this->_m_current << 8);
        }
    }

    /// @brief get a accumulate value once
    virtual inline auto accumulated () noexcept -> STDINT override {
        if (this->_m_refOut)
            this->_m_current = CRC_Calculator_ct::reflect (this->_m_current, sizeof (STDINT) * 8);
        return this->_m_current ^ this->_m_xor_out;
    }

    // clang-format off
        virtual inline auto getRefIn () const noexcept -> bool { return this->_m_refIn; }
        virtual inline auto getRefOut () const noexcept -> bool { return this->_m_refOut; }
        virtual inline auto getPloy () const noexcept -> STDINT { return this->_m_ploy; }
        virtual inline auto getInit () const noexcept -> STDINT { return this->_m_init; }
        virtual inline auto getCurrent () const noexcept -> STDINT { return this->_m_current; }
        virtual inline auto getXOROut () const noexcept -> STDINT { return this->_m_xor_out; }
        virtual inline auto getTable () const noexcept -> STDINT * { return const_cast<STDINT *> (this->_m_table); }

        virtual inline auto resetRefIn (bool _refIn) noexcept -> void { this->_m_refIn = _refIn; }
        virtual inline auto resetRefOut (bool _refOut) noexcept -> void { this->_m_refOut = _refOut; }
        virtual inline auto resetPloy (STDINT _ploy) noexcept -> void { this->_m_ploy = _ploy; }
        virtual inline auto resetInit (STDINT _init) noexcept -> void { this->_m_init = _init; }
        virtual inline auto resetCurrent () noexcept -> void override { this->_m_current = this->_m_init; }
        virtual inline auto resetXOROut (STDINT _xor_out) noexcept -> void { this->_m_xor_out = _xor_out; }

    // clang-format on


  protected:
    bool   _m_refIn      = false;
    bool   _m_refOut     = false;
    STDINT _m_ploy       = 0;
    STDINT _m_init       = 0;
    STDINT _m_current    = 0;
    STDINT _m_xor_out    = 0;
    STDINT _m_table[256] = {};
};

class CRC64_Calculator_ct : public CRC_Calculator_ct<std::uint64_t> {
  public:
    explicit CRC64_Calculator_ct (const CRC64_Calculator_ct & _other) noexcept    = default;
    explicit CRC64_Calculator_ct (CRC64_Calculator_ct && _other) noexcept         = default;
    CRC64_Calculator_ct & operator= (const CRC64_Calculator_ct & _other) noexcept = default;
    CRC64_Calculator_ct & operator= (CRC64_Calculator_ct && _other) noexcept      = default;
    explicit CRC64_Calculator_ct () noexcept                                      = default;
    ~CRC64_Calculator_ct () noexcept                                              = default;

    explicit CRC64_Calculator_ct (std::uint64_t _ploy, std::uint64_t _init, std::uint64_t _xorOut, bool _refIn, bool _refOut) noexcept :
        CRC_Calculator_ct<std::uint64_t> {_ploy, _init, _xorOut, _refIn, _refOut} {}
};

class CRC32_Calculator_ct : public CRC_Calculator_ct<std::uint32_t> {
  public:
    explicit CRC32_Calculator_ct (const CRC32_Calculator_ct & _other) noexcept    = default;
    explicit CRC32_Calculator_ct (CRC32_Calculator_ct && _other) noexcept         = default;
    CRC32_Calculator_ct & operator= (const CRC32_Calculator_ct & _other) noexcept = default;
    CRC32_Calculator_ct & operator= (CRC32_Calculator_ct && _other) noexcept      = default;
    explicit CRC32_Calculator_ct () noexcept                                      = default;
    ~CRC32_Calculator_ct () noexcept                                              = default;

    explicit CRC32_Calculator_ct (std::uint32_t _ploy, std::uint32_t _init, std::uint32_t _xorOut, bool _refIn, bool _refOut) noexcept :
        CRC_Calculator_ct<std::uint32_t> {_ploy, _init, _xorOut, _refIn, _refOut} {}
};

class CRC16_Calculator_ct : public CRC_Calculator_ct<std::uint16_t> {
  public:
    explicit CRC16_Calculator_ct (const CRC16_Calculator_ct & _other) noexcept    = default;
    explicit CRC16_Calculator_ct (CRC16_Calculator_ct && _other) noexcept         = default;
    CRC16_Calculator_ct & operator= (const CRC16_Calculator_ct & _other) noexcept = default;
    CRC16_Calculator_ct & operator= (CRC16_Calculator_ct && _other) noexcept      = default;
    explicit CRC16_Calculator_ct () noexcept                                      = default;
    ~CRC16_Calculator_ct () noexcept                                              = default;

    explicit CRC16_Calculator_ct (std::uint16_t _ploy, std::uint16_t _init, std::uint16_t _xorOut, bool _refIn, bool _refOut) noexcept :
        CRC_Calculator_ct<std::uint16_t> {_ploy, _init, _xorOut, _refIn, _refOut} {}
};

class CRC8_Calculator_ct : public CRC_Calculator_ct<std::uint8_t> {
  public:
    explicit CRC8_Calculator_ct (const CRC8_Calculator_ct & _other) noexcept    = default;
    explicit CRC8_Calculator_ct (CRC8_Calculator_ct && _other) noexcept         = default;
    CRC8_Calculator_ct & operator= (const CRC8_Calculator_ct & _other) noexcept = default;
    CRC8_Calculator_ct & operator= (CRC8_Calculator_ct && _other) noexcept      = default;
    explicit CRC8_Calculator_ct () noexcept                                     = default;
    ~CRC8_Calculator_ct () noexcept                                             = default;

    explicit CRC8_Calculator_ct (std::uint8_t _ploy, std::uint8_t _init, std::uint8_t _xorOut, bool _refIn, bool _refOut) noexcept :
        CRC_Calculator_ct<std::uint8_t> {_ploy, _init, _xorOut, _refIn, _refOut} {}
};

template <typename STDINT>
class AccuSum_Calculator_ct : public Verification_Calculator_ct<STDINT> {
  public:
    inline auto calculate (const std::uint8_t _data[], unsigned _length) noexcept -> STDINT override {
        (void) this->resetCurrent ();
        (void) this->accumulate (_data, _length);
        return this->accumulated ();
    }

    inline auto resetCurrent () noexcept -> void override { this->_m_sum = 0; }

    inline auto accumulated () noexcept -> STDINT override { return this->_m_sum; }

    inline auto accumulate (const std::uint8_t _data[], unsigned _length) noexcept -> void override {
        if (_data == nullptr || _length <= 0) return;
        for (unsigned i = 0; i < _length; ++i)
            this->_m_sum += _data[i];
    }

  private:
    STDINT _m_sum = 0;
};

template <typename STDINT>
class XorSum_Calculator_ct : public Verification_Calculator_ct<STDINT> {
  public:
    inline auto calculate (const std::uint8_t _data[], unsigned _length) noexcept -> STDINT override {
        (void) this->resetCurrent ();
        (void) this->accumulate (_data, _length);
        return this->accumulated ();
    }

    inline auto resetCurrent () noexcept -> void override { this->_m_sum = 0; }

    inline auto accumulated () noexcept -> STDINT override { return this->_m_sum; }

    inline auto accumulate (const std::uint8_t _data[], unsigned _length) noexcept -> void override {
        if (_data == nullptr || _length <= 0) return;
        for (auto i = 0; i < _length; ++i)
            this->_m_sum ^= _data[i];
    }

  private:
    STDINT _m_sum = 0;
};


#endif
